package no.ntnu.fp.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.sql.Time;
import java.sql.Date;
import java.util.Map;

import javax.imageio.spi.RegisterableService;
import javax.management.RuntimeErrorException;
import javax.swing.JOptionPane;
import javax.swing.ListModel;
import javax.swing.event.ListDataListener;

import no.ntnu.fp.client.ClientConnectionHandler;
import no.ntnu.fp.client.ClientModelHandler;
import no.ntnu.fp.model.Message.MessageType;

import org.apache.commons.lang.StringUtils;

public class Appointment extends AbstractModel implements ListModel {
	private static Map<Integer, Appointment> instances = new HashMap<Integer, Appointment>();

	public enum RSVP {
		ATTENDING("M�ter"), NOT_ATTENDING("M�ter ikke"), UNDECIDED("Ubesvart");

		private final String text;

		private RSVP(String text) {
			this.text = text;
		}

		public String getText() {
			return text;
		}
	}

	public enum AppointmentType {
		PERSONAL, WORK
	}

	public Appointment() {
		// Request
		id = requestId();
		type = AppointmentType.PERSONAL;
		inviteList = new HashMap<Integer, RSVP>();
	}

	public boolean isNew() {
		return id <= -1;
	}

	// public static Appointment example() {
	// Appointment ex = new Appointment();
	// ex.title = "Morgenm�te";
	// ex.description = "Diskutere saker og ting med hele gjengen.";
	// ex.startDate = new Date(2011, 5, 20);
	// ex.endDate = new Date(2011, 5, 20);
	// ex.start = new Time(11, 30, 00);
	// ex.end = new Time(13, 00, 00);
	// ex.guestCount = 3;
	// ex.room = 2;
	// ex.place = "cafe noir";
	// ex.owner = 1;
	// ex.invite(Person.get(1));
	// return ex;
	// }

	public static void makeExamples() {
		{
			System.out.println("i made examles");
			Appointment anAppointment = new Appointment();
			// anAppointment.setOwner(Person.all().get(0));
			anAppointment.setOwner(Person.getCurrentUser());
			anAppointment.title = "Morgenm�te";
			anAppointment.description = "Diskutere saker og ting med hele gjengen.";
			anAppointment.startDate = new Date(111, 3, 20);
			anAppointment.endDate = new Date(111, 3, 20);
			anAppointment.start = new Time(11, 30, 00);
			anAppointment.end = new Time(13, 00, 00);
			anAppointment.guestCount = 3;
			anAppointment.room = 2;
			anAppointment.place = "cafe noir";
			anAppointment.register(anAppointment.id);

		}

		{
			Appointment anAppointment = new Appointment();
			anAppointment.title = "Aftenm�te";
			// anAppointment.setOwner(Person.all().get(0));
			anAppointment.setOwner(Person.getCurrentUser());
			anAppointment.description = "Diskutere saker og ting med hele gjengen.";
			anAppointment.startDate = new Date(111, 4, 2);
			anAppointment.endDate = new Date(111, 4, 2);
			anAppointment.start = new Time(11, 30, 00);
			anAppointment.end = new Time(13, 00, 00);
			anAppointment.guestCount = 3;
			anAppointment.room = 2;
			anAppointment.place = "cafe noir";
			anAppointment.register(anAppointment.id);
			// anAppointment.invite(Person.all().get(0));
		}
	}

	/**
	 * Stephan trenger denne til testing av kalender panelet.
	 */
	public static Appointment example(int startTimeHour, int statTimeMinute,
			int endTimeHour, int endTimeMinute, int date) {
		System.out.println("i made examle");
		Appointment ex = new Appointment();
		ex.title = "Morgenm�te";
		ex.description = "Diskutere saker og ting med hele gjengen.";
		ex.startDate = new Date(111, 5, date);
		ex.endDate = new Date(111, 5, date);
		ex.start = new Time(startTimeHour, statTimeMinute, 00);
		ex.end = new Time(endTimeHour, endTimeMinute, 00);
		ex.guestCount = 3;
		ex.room = 2;
		ex.place = "cafe noir";
		ex.owner = 1;
		return ex;
	}

	public Date startDate;
	public Date endDate;

	public boolean alive;

	public Map<Integer, RSVP> inviteList;

	public Time start;

	public Time end;

	public String title = "";

	public String place = "";

	public int owner;

	public int room;

	public int guestCount;

	public AppointmentType type;

	public String description;

	public static Appointment blank() {
		Appointment appointment = new Appointment();
		appointment.start = new Time(java.util.Calendar.getInstance()
				.getTimeInMillis());
		appointment.end = new Time(java.util.Calendar.getInstance()
				.getTimeInMillis() + 1000 * 60 * 60);
		appointment.startDate = new Date(java.util.Calendar.getInstance()
				.getTimeInMillis());
		appointment.endDate = new Date(java.util.Calendar.getInstance()
				.getTimeInMillis());
		if (Room.all().size() < 1) {
			Room.makeExampleRooms();
		}
		appointment.room = Room.all().get(0).id;
		appointment.register(appointment.id);
		return appointment;
	}

	public Date getStartDate() {
		return startDate;
	}

	public void setStartDate(Date date) {
		this.startDate = date;
	}

	public Date getEndDate() {
		return endDate;
	}

	public void setEndate(Date date) {
		this.endDate = date;
	}

	public Time getStart() {
		return start;
	}

	public void setStart(Time start) {
		this.start = start;
	}

	public Time getEnd() {
		return end;
	}

	public void setEnd(Time end) {
		this.end = end;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public Person getOwner() {
		return Person.get(owner);
	}

	public void setOwner(Person owner) {
		this.owner = owner.getId();
	}

	public AppointmentType getType() {
		return type;
	}

	public Room getRoom() {
		return Room.get(room);
	}

	public void setRoom(Room room) {
		this.room = room.getId();
	}

	@Override
	public Map<String, Object> pack() {
		Map<String, Object> m = createAttributeMap();
		m.put("title", title);
		m.put("description", description);
		m.put("start", start.getTime());
		m.put("end", end.getTime());
		m.put("startDate", startDate.getTime());
		m.put("endDate", endDate.getTime());
		m.put("room", room);
		m.put("owner", owner);
		m.put("alive", alive);
		m.put("place", place);
		m.put("type", type.toString());
		m.put("guestCount", guestCount);
		m.put("people", getIdList());
		return m;
	}

	@Override
	public void writeAttribute(String attribute, Object value) {

		if (attribute.equals("title")) {
			title = (String) value;
		} else if (attribute.equals("description")) {
			description = (String) value;
		} else if (attribute.equals("startDate")) {
			startDate = new Date(Long.parseLong((String) value));
		} else if (attribute.equals("endDate")) {
			endDate = new Date(Long.parseLong((String) value));
		} else if (attribute.equals("start")) {
			start = new Time(Long.parseLong((String) value));
		} else if (attribute.equals("end")) {
			end = new Time(Long.parseLong((String) value));
		} else if (attribute.equals("room")) {
			room = Integer.parseInt((String) value);
		} else if (attribute.equals("alive")) {
			alive = Boolean.parseBoolean((String) value);
		} else if (attribute.equals("place")) {
			place = (String) value;
		} else if (attribute.equals("owner")) {
			owner = Integer.parseInt((String) value);
		} else if (attribute.equals("type")) {
			type = AppointmentType.valueOf((String) value);
		} else if (attribute.equals("guestCount")) {
			guestCount = Integer.parseInt((String) value);
		} else if (attribute.equals("people")) {
			setInviteList((String) value);
		}
	}

	// pelle
	public void setInviteList(String value) {
		inviteList = new HashMap<Integer, RSVP>();
		for (String entry : StringUtils.split(value, ",")) {
			String[] tuple = StringUtils.split(entry, "=");
			int id = Integer.parseInt(tuple[0]);
			RSVP rsvp = RSVP.valueOf(tuple[1]);

			inviteList.put(id, rsvp);
		}
	}

	/**
	 * Lager en liste med personer p� formatet:
	 * <code>3=ATTENDING,4=NOT_ATTENDING,7=UNDECIDED,10=ATTENDING</code>
	 * 
	 * @return en {@code String} av formatet over
	 */
	public String getIdList() {
		List<String> list = new ArrayList<String>();
		for (int id : inviteList.keySet()) {
			list.add(id + "=" + inviteList.get(id));
		}

		return StringUtils.join(list, ',');
	}

	public List<Person> getAttending() {
		List<Person> list = new ArrayList<Person>();
		for (int id : inviteList.keySet()) {
			if (inviteList.get(id) == RSVP.ATTENDING) {
				list.add(Person.get(id));
			}
		}
		return list;
	}

	//
	public List<Person> getNotAttending() {
		List<Person> list = new ArrayList<Person>();
		for (int id : inviteList.keySet()) {
			if (inviteList.get(id) == RSVP.NOT_ATTENDING) {
				list.add(Person.get(id));
			}
		}
		return list;
	}

	public List<Person> getUndecided() {
		List<Person> list = new ArrayList<Person>();
		for (int id : inviteList.keySet()) {
			if (inviteList.get(id) == RSVP.UNDECIDED) {
				list.add(Person.get(id));
			}
		}
		return list;
	}

	public int getGuestCount() {
		return guestCount;
	}

	public void setGuestCount(int count) {
		this.guestCount = count;
	}

	public void setType(AppointmentType type) {
		if (type == null)
			throw new IllegalArgumentException(
					"AppointmentType must be non-null");
		this.type = type;
	}

	public void invite(Person participant) {
		inviteList.put(participant.id, RSVP.UNDECIDED);
	}

	public void uninvite(Person participant) {
		inviteList.remove(id);
	}

	public static List<Appointment> all() {
		List<Appointment> all = new ArrayList<Appointment>();
		for (Map.Entry<Integer, Appointment> entry : instances.entrySet()) {
			Appointment appointment = entry.getValue();
			all.add(appointment);
		}
		return all;
	}
	
	public void generateMessages(MessageType type) {
		
		List<Message> messages = new LinkedList<Message>();
		for (int i : inviteList.keySet()) {
			messages.add(new Message(this, Person.get(i), type));
		}
		
		ClientModelHandler.sendMessages(messages);
	}

	/**
	 * Passer p� at inviteList blir riktig og at meldinger blir opprettet til
	 * folk. Denne metoden kallles fra GUI etter at en avtale er blitt
	 * opprettet/endret.
	 * 
	 * @param newList
	 *            den nye invitasjonslisten
	 * @return liste med alle de nye genererte meldingene
	 */
	public void updateInviteList(List<Person> newList) {

			if (inviteList.size() == 0) {
				inviteList.put(Person.getCurrentUser().id, RSVP.ATTENDING);
				for (Person p : newList) {
					inviteList.put(p.id, RSVP.UNDECIDED);
				}
			}
			else {
				for (int i = 0; i < newList.size(); i++) {
					Person p = newList.get(i);
					if (inviteList.containsKey(p.id)) {
						// er allerede invitert
					} else {
						Message m = new Message(this, p, MessageType.INVITE);
						ClientModelHandler.updateModel(m);
					}
				}
	
				for (Map.Entry<Integer, RSVP> entry : inviteList.entrySet()) {
					Integer personId = entry.getKey();
					RSVP rsvp = entry.getValue();
					if (newList.contains(Person.get(personId))) {
						// sett p�
					} else {
						// Denne persoenn er fjerent fra Invite List
						Message m = new Message(this, Person.get(personId),
							MessageType.DEINVITE);
						ClientModelHandler.updateModel(m);
					}
				}
			}
		
	}

	@Override
	public void addListDataListener(ListDataListener l) {
		// TODO Add'e her
	}

	@Override
	public Object getElementAt(int index) {
		// TODO : How??
		return inviteList.get(1); // random
	}

	@Override
	public int getSize() {
		return inviteList.size();
	}

	@Override
	public void removeListDataListener(ListDataListener l) {
	}

	public static Appointment get(int id) {
		return instances.get(id);
	}

	public void register(int id) {
		instances.put(id, this);
	}

	public void setPlace(String place) {
		this.place = place;
	}

	public String getPlace() {
		return place;
	}

	public void setRSVP(RSVP answer, Person p) {
		inviteList.put(p.getId(), answer);
		ClientModelHandler.updateModel(this);
	}

	public RSVP getRSVP(Person p) {
		return inviteList.get(p);
	}

	public void setAlive(boolean alive) {
		this.alive = alive;
	}

	public boolean isAlive() {
		return alive;
	}

	public static List<Appointment> getAppointmentsFor(Person person) {
		List<Appointment> list = new ArrayList<Appointment>();

		for (Integer id : instances.keySet()) {

			Appointment appointment = instances.get(id);
			if (appointment.owner == person.id
					|| appointment.getAttending().contains(person)) {
				if (person == null) {
					System.err.println("rtansieotrniheon");
				}
				if (appointment != null) {
					System.err.println(appointment);
					list.add(appointment);
				}
			}
		}
		return list;
	}

	/**
	 * Finner alle avtaler for en person {@code person} mellom {@code from} og
	 * {@code to}
	 */
	public static List<Appointment> getAppointmentsBetween(Person person,
			Date from, Date to) {
		List<Appointment> list = new ArrayList<Appointment>();
		for (Map.Entry<Integer, Appointment> entry : instances.entrySet()) {
			Appointment appointment = entry.getValue();
			// if ((((from.getYear() == appointment.startDate.getYear()&&
			// from.getDate()==appointment.startDate.getDate() &&
			// from.getMonth()==appointment.startDate.getMonth())||from.before(appointment.startDate)
			// )&& to.after(appointment.endDate))
			// && (appointment.owner == person.id ||
			// appointment.getAttending().contains(person))) {
			// System.out.println("Owner: " + appointment.owner +
			// ". Person id: "
			// + person);
			if ((((from.getYear() == appointment.startDate.getYear()
					&& from.getDate() == appointment.startDate.getDate() && from
					.getMonth() == appointment.startDate.getMonth()) || from
					.before(appointment.startDate)) && to
					.after(appointment.endDate))
					&& (appointment.owner == person.id
							|| appointment.getAttending().contains(person) || appointment
							.getUndecided().contains(person))) {
				list.add(appointment);
			}
		}

		return list;
	}

	public boolean collidesWith(Appointment other) {

		if (other.getStartDate().after(this.getEndDate())
				|| other.getEndDate().before(this.getStartDate())) {
			// can not collide
			return false;
		} else if (other.getStartDate().compareTo(this.getEndDate()) == 0) {
			// appointments might collide
			if (other.getStart().after(this.getEnd())) {
				return false;
			}
		} else if (other.getEndDate().compareTo(this.getStartDate()) == 0) {
			// appointments might collide
			if (other.getEnd().before(this.getStart())) {
				return false;
			}
		}
		// else, appointments collide
		return true;
	}
}
